/*******************************************************\
|* Author: Trevor Sundberg
\*******************************************************/

// Header protection
#pragma once
#ifndef SKUGO_METHOD_BINDING_HPP
#define SKUGO_METHOD_BINDING_HPP

// Includes
#include "SkugoTypeBinding.hpp"
#include "SkugoFunctionHelpers.hpp"

namespace Skugo
{
  // Using directives
  using std::vector;

  // All things relevant to binding methods
  class MethodBinding
  {
  public:

    // Add a method to a type
    static AttributeContainer& Add(vector<MethodInfo>& list, const MethodInfo& method);

    // Include all the binding code
    #include "SkugoMethodBinding.inl"
  };

  // Binding helpers
  #define SkugoMethodHelperGivenType(methodKind, methodType, methodPtr, name) MethodBinding::Add(type.methodKind##Methods, MethodBinding::From##methodKind<methodType, methodPtr>(name, (methodType)methodPtr))
  #define SkugoMethodHelperInferType(methodKind, methodPtr, name)             SkugoMethodHelperGivenType(methodKind, decltype(methodPtr), methodPtr, name)

  // Bind a method (standard)
  #define SkugoBindInstanceMethod(method)                                     SkugoBindInstanceMethodAs(method, #method)
  #define SkugoBindStaticMethod(method)                                       SkugoBindStaticMethodAs(  method, #method)

  // Bind a method with a different name
  #define SkugoBindInstanceMethodAs(method, name)                             SkugoMethodHelperInferType(Instance, &ThisType::method, name)
  #define SkugoBindStaticMethodAs(method, name)                               SkugoMethodHelperInferType(Static,   &ThisType::method, name)

  // Bind a method that is overloaded
  #define SkugoBindInstanceMethodOverload(method, returnType, ...)            SkugoBindInstanceMethodOverloadAs(method, #method, returnType, __VA_ARGS__)
  #define SkugoBindStaticMethodOverload(method, returnType, ...)              SkugoBindStaticMethodOverloadAs(  method, #method, returnType, __VA_ARGS__)

  // Bind a method that is overloaded with a different name
  #define SkugoBindInstanceMethodOverloadAs(method, name, returnType, ...)    SkugoMethodHelperGivenType(Instance, returnType (ThisType::*)(__VA_ARGS__), &ThisType::method, name)
  #define SkugoBindStaticMethodOverloadAs(method, name, returnType, ...)      SkugoMethodHelperGivenType(Static,   returnType (*)(__VA_ARGS__),           &ThisType::method, name)

  // Aliases for the above macros (for shortening the length of code without losing readability)
  #define SkugoBindMethod(method)                                             SkugoBindInstanceMethod(method)
  #define SkugoBindMethodAs(method, name)                                     SkugoBindInstanceMethodAs(method, name)
  #define SkugoBindMethodOverload(method, returnType, ...)                    SkugoBindInstanceMethodOverload(method, returnType, __VA_ARGS__)
  #define SkugoBindMethodOverloadAs(method, name, returnType, ...)            SkugoBindInstanceMethodOverloadAs(method, name, returnType, __VA_ARGS__)
}

// End header protection
#endif
